package com.geek.novel.ui

import android.content.Context
import android.graphics.*
import android.graphics.PorterDuff.Mode
import android.os.Build
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.DecelerateInterpolator
import android.view.animation.Transformation
import android.widget.FrameLayout
import com.geek.novel.R
import java.util.*

class SlideHolder : FrameLayout {

    private var mCachedBitmap: Bitmap? = null
    private var mCachedCanvas: Canvas? = null
    private var mCachedPaint: Paint? = null
    private var mMenuView: View? = null

    private var mMode = MODE_READY
    private var mDirection = DIRECTION_LEFT

    var menuOffset = 0
        private set
    private var mStartOffset: Int = 0
    private var mEndOffset: Int = 0

    private var mEnabled = true
    var isAllowedInterceptTouch = false
        private set
    private var mAlwaysOpened = false
    /**

     * @param dispatch - if true, in open state SlideHolder will dispatch touch events to main layout (in other words - it will be clickable)
     */
    var isDispatchTouchWhenOpened = false

    private val mWhenReady = LinkedList<Runnable>()

    private var mListener: OnSlideListener? = null

    constructor(context: Context) : super(context) {
        initView()
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {

        initView()
    }

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {

        initView()
    }

    private fun initView() {
        mCachedPaint = Paint(
                Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG or Paint.DITHER_FLAG
        )
    }

    override fun setEnabled(enabled: Boolean) {
        mEnabled = enabled
    }

    override fun isEnabled(): Boolean {
        return mEnabled
    }

    /**

     * @param direction - direction in which SlideHolder opens. Can be: DIRECTION_LEFT, DIRECTION_RIGHT
     */
    fun setDirection(direction: Int) {
        closeImmediately()

        mDirection = direction
    }

    /**

     * @param allow - if false, SlideHolder won't react to swiping gestures (but still will be able to work by manually invoking mathods)
     */
    fun setAllowInterceptTouch(allow: Boolean) {
        isAllowedInterceptTouch = allow
    }

    /**

     * @param opened - if true, SlideHolder will always be in opened state (which means that swiping won't work)
     */
    fun setAlwaysOpened(opened: Boolean) {
        mAlwaysOpened = opened

        requestLayout()
    }

    fun setOnSlideListener(lis: OnSlideListener) {
        mListener = lis
    }

    val isOpened: Boolean
        get() = mAlwaysOpened || mMode == MODE_FINISHED

    fun toggle(immediately: Boolean) {
        if (immediately) {
            toggleImmediately()
        } else {
            toggle()
        }
    }

    fun toggle() {
        if (isOpened) {
            close()
        } else {
            open()
        }
    }

    private fun toggleImmediately() {
        if (isOpened) {
            closeImmediately()
        } else {
            openImmediately()
        }
    }

    fun open(): Boolean {
        if (isOpened || mAlwaysOpened || mMode == MODE_SLIDE) {
            return false
        }

        if (!isReadyForSlide) {
            mWhenReady.add(Runnable { open() })

            return true
        }

        initSlideMode()

        val anim = SlideAnimation(menuOffset.toFloat(), mEndOffset.toFloat())
        anim.setAnimationListener(mOpenListener)
        startAnimation(anim)

        toggleMask(true)

        invalidate()

        return true
    }

	
	inner class AlphaAnimationListener(var maskView: View, var show: Boolean) : Animation.AnimationListener{
		override fun onAnimationRepeat(animation: Animation?) {
		}
		override fun onAnimationEnd(animation: Animation?) {
			maskView.visibility = if(show) View.VISIBLE else View.GONE
			maskView.alpha = 1f
		}
		override fun onAnimationStart(animation: Animation?) {
			maskView.visibility = View.VISIBLE
		}
		
	}
	
    /**阴影层*/
    private fun toggleMask(show: Boolean) {
        val v = getChildAt(1)
        val maskView: View = v.findViewById(R.id.view_mask)
        val startAlpha: Float?
        val endAlpha: Float?
		if (show) {
            startAlpha = 0f
            endAlpha = 1f
        } else {
            startAlpha = 1f
            endAlpha = 0f
        }
        val anim = AlphaAnimation(startAlpha, endAlpha)
        anim.duration = 300
	
		anim.setAnimationListener(AlphaAnimationListener(maskView, show))
		
        maskView.startAnimation(anim)

    }

    fun openImmediately(): Boolean {
        if (isOpened || mAlwaysOpened || mMode == MODE_SLIDE) {
            return false
        }

        if (!isReadyForSlide) {
            mWhenReady.add(Runnable { openImmediately() })

            return true
        }

        mMenuView!!.visibility = View.VISIBLE
        mMode = MODE_FINISHED
        requestLayout()

        if (mListener != null) {
            mListener!!.onSlideCompleted(true)
        }

        return true
    }

    fun close(): Boolean {
        if (!isOpened || mAlwaysOpened || mMode == MODE_SLIDE) {
            return false
        }

        if (!isReadyForSlide) {
            mWhenReady.add(Runnable { close() })

            return true
        }

        initSlideMode()


        val anim = SlideAnimation(menuOffset.toFloat(), mEndOffset.toFloat())
        anim.setAnimationListener(mCloseListener)
        startAnimation(anim)

        toggleMask(false)

        invalidate()

        return true
    }

    private fun closeImmediately(): Boolean {
        if (!isOpened || mAlwaysOpened || mMode == MODE_SLIDE) {
            return false
        }

        if (!isReadyForSlide) {
            mWhenReady.add(Runnable { closeImmediately() })

            return true
        }

        mMenuView!!.visibility = View.GONE
        mMode = MODE_READY
        requestLayout()

        if (mListener != null) {
            mListener!!.onSlideCompleted(false)
        }

        return true
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        val parentLeft = 0
        val parentTop = 0
        val parentRight = r - l
        val parentBottom = b - t

        val menu = getChildAt(0)
        val menuWidth = menu.measuredWidth

        if (mDirection == DIRECTION_LEFT) {
            menu.layout(parentLeft, parentTop, parentLeft + menuWidth, parentBottom)
        } else {
            menu.layout(parentRight - menuWidth, parentTop, parentRight, parentBottom)
        }

        if (mAlwaysOpened) {
            if (mDirection == DIRECTION_LEFT) {
                menuOffset = menuWidth
            } else {
                menuOffset = 0
            }
        } else if (mMode == MODE_FINISHED) {
            menuOffset = mDirection * menuWidth
        } else if (mMode == MODE_READY) {
            menuOffset = 0
        }

        val main = getChildAt(1)
        main.layout(
                parentLeft + menuOffset,
                parentTop,
                parentLeft + menuOffset + main.measuredWidth,
                parentBottom
        )

        invalidate()


        while (true) {
            val rn = mWhenReady.poll()
            if (rn == null) {
                break
            }
            rn.run()
        }
    }

    private val isReadyForSlide: Boolean
        get() = width > 0 && height > 0

    override fun onMeasure(wSp: Int, hSp: Int) {
        mMenuView = getChildAt(0)

        if (mAlwaysOpened) {
            val main = getChildAt(1)

            if (mMenuView != null && main != null) {
                measureChild(mMenuView, wSp, hSp)
                val lp = main.layoutParams as FrameLayout.LayoutParams

                if (mDirection == DIRECTION_LEFT) {
                    lp.leftMargin = mMenuView!!.measuredWidth
                } else {
                    lp.rightMargin = mMenuView!!.measuredWidth
                }
            }
        }

        super.onMeasure(wSp, hSp)
    }

    private var mFrame: Byte = 0

    override fun dispatchDraw(canvas: Canvas) {
        try {
            if (mMode == MODE_SLIDE) {
                val main = getChildAt(1)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                    /*
                     * On new versions we redrawing main layout only
					 * if it's marked as dirty
					 */
                    if (main.isDirty) {
                        mCachedCanvas!!.drawColor(Color.TRANSPARENT, Mode.CLEAR)
                        main.draw(mCachedCanvas)
                    }
                } else {
                    /*
					 * On older versions we just redrawing our cache
					 * every 5th frame
					 */
                    if (++mFrame % 5 == 0) {
                        mCachedCanvas!!.drawColor(Color.TRANSPARENT, Mode.CLEAR)
                        main.draw(mCachedCanvas)
                    }
                }

                /*
				 * Draw only visible part of menu
				 */

                val menu = getChildAt(0)
                val scrollX = menu.scrollX
                val scrollY = menu.scrollY

                canvas.save()

                if (mDirection == DIRECTION_LEFT) {
//                    canvas.clipRect(0f, 0f, menuOffset.toFloat(), menu.height.toFloat(), Op.REPLACE)
                    canvas.clipRect(0f, 0f, menuOffset.toFloat(), menu.height.toFloat())
                } else {
                    val menuWidth = menu.width
                    val menuLeft = menu.left

                    canvas.clipRect(menuLeft + menuWidth + menuOffset, 0, menuLeft + menuWidth, menu.height)
                }

                canvas.translate(menu.left.toFloat(), menu.top.toFloat())
                canvas.translate((-scrollX).toFloat(), (-scrollY).toFloat())

                menu.draw(canvas)

                canvas.restore()

                canvas.drawBitmap(mCachedBitmap!!, menuOffset.toFloat(), 0f, mCachedPaint)
            } else {
                if (!mAlwaysOpened && mMode == MODE_READY) {
                    mMenuView!!.visibility = View.GONE
                }

                super.dispatchDraw(canvas)
            }
        } catch (e: IndexOutOfBoundsException) {
            /*
			 * Possibility of crashes on some devices (especially on Samsung).
			 * Usually, when ListView is empty.
			 */
        }

    }

    private var mHistoricalX = 0
    private var mCloseOnRelease = false

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        if ((!mEnabled || !isAllowedInterceptTouch) && mMode == MODE_READY || mAlwaysOpened) {
            return super.dispatchTouchEvent(ev)
        }

        if (mMode != MODE_FINISHED) {
            onTouchEvent(ev)

            if (mMode != MODE_SLIDE) {
                super.dispatchTouchEvent(ev)
            } else {
                val cancelEvent = MotionEvent.obtain(ev)
                cancelEvent.action = MotionEvent.ACTION_CANCEL
                super.dispatchTouchEvent(cancelEvent)
                cancelEvent.recycle()
            }

            return true
        } else {
            val action = ev.action

            val rect = Rect()
            val menu = getChildAt(0)
            menu.getHitRect(rect)

            if (!rect.contains(ev.x.toInt(), ev.y.toInt())) {
                if (action == MotionEvent.ACTION_UP && mCloseOnRelease && !isDispatchTouchWhenOpened) {
                    close()
                    mCloseOnRelease = false
                } else {
                    if (action == MotionEvent.ACTION_DOWN && !isDispatchTouchWhenOpened) {
                        mCloseOnRelease = true
                    }

                    onTouchEvent(ev)
                }

                if (isDispatchTouchWhenOpened) {
                    super.dispatchTouchEvent(ev)
                }

                return true
            } else {
                onTouchEvent(ev)

                ev.offsetLocation((-menu.left).toFloat(), (-menu.top).toFloat())
                menu.dispatchTouchEvent(ev)

                return true
            }
        }
    }

    private fun handleTouchEvent(ev: MotionEvent): Boolean {
        if (!mEnabled) {
            return false
        }

        val x = ev.x

        if (ev.action == MotionEvent.ACTION_DOWN) {
            mHistoricalX = x.toInt()

            return true
        }

        if (ev.action == MotionEvent.ACTION_MOVE) {

            val diff = x - mHistoricalX

            if (mDirection * diff > 50 && mMode == MODE_READY || mDirection * diff < -50 && mMode == MODE_FINISHED) {
                mHistoricalX = x.toInt()

                initSlideMode()
            } else if (mMode == MODE_SLIDE) {
                menuOffset += diff.toInt()

                mHistoricalX = x.toInt()

                if (!isSlideAllowed) {
                    finishSlide()
                }
            } else {
                return false
            }
        }

        if (ev.action == MotionEvent.ACTION_UP) {
            if (mMode == MODE_SLIDE) {
                finishSlide()
            }

            mCloseOnRelease = false

            return false
        }

        return mMode == MODE_SLIDE
    }

    override fun onTouchEvent(ev: MotionEvent): Boolean {
        val handled = handleTouchEvent(ev)

        invalidate()

        return handled
    }

    private fun initSlideMode() {
        mCloseOnRelease = false

        val v = getChildAt(1)

        if (mMode == MODE_READY) {
            mStartOffset = 0
            mEndOffset = mDirection * getChildAt(0).width
        } else {
            mStartOffset = mDirection * getChildAt(0).width
            mEndOffset = 0
        }

        menuOffset = mStartOffset

        if (mCachedBitmap == null || mCachedBitmap!!.isRecycled || mCachedBitmap!!.width != v.width) {
            mCachedBitmap = Bitmap.createBitmap(v.width, v.height, Bitmap.Config.ARGB_8888)
            mCachedCanvas = Canvas(mCachedBitmap!!)
        } else {
            mCachedCanvas!!.drawColor(Color.TRANSPARENT, Mode.CLEAR)

        }

        v.visibility = View.VISIBLE

        mCachedCanvas!!.translate((-v.scrollX).toFloat(), (-v.scrollY).toFloat())
        v.draw(mCachedCanvas)

        mMode = MODE_SLIDE

        mMenuView!!.visibility = View.VISIBLE
    }

    private val isSlideAllowed: Boolean
        get() = mDirection * mEndOffset > 0 && mDirection * menuOffset < mDirection * mEndOffset && mDirection * menuOffset >= mDirection * mStartOffset || mEndOffset == 0 && mDirection * menuOffset > mDirection * mEndOffset && mDirection * menuOffset <= mDirection * mStartOffset

    private fun completeOpening() {
        menuOffset = mDirection * mMenuView!!.width
        requestLayout()

        post {
            mMode = MODE_FINISHED
            mMenuView!!.visibility = View.VISIBLE
        }

        if (mListener != null) {
            mListener!!.onSlideCompleted(true)
        }
    }

    private val mOpenListener = object : Animation.AnimationListener {

        override fun onAnimationStart(animation: Animation) {}

        override fun onAnimationRepeat(animation: Animation) {}

        override fun onAnimationEnd(animation: Animation) {
            completeOpening()
        }
    }

    private fun completeClosing() {
        menuOffset = 0
        requestLayout()

        post {
            mMode = MODE_READY
            mMenuView!!.visibility = View.GONE
        }

        if (mListener != null) {
            mListener!!.onSlideCompleted(false)
        }
    }

    private val mCloseListener = object : Animation.AnimationListener {

        override fun onAnimationStart(animation: Animation) {}

        override fun onAnimationRepeat(animation: Animation) {}

        override fun onAnimationEnd(animation: Animation) {
            completeClosing()
        }
    }

    private fun finishSlide() {
        if (mDirection * mEndOffset > 0) {//拖拽打开菜单
            if (mDirection * menuOffset > mDirection * mEndOffset / 5) {
                if (mDirection * menuOffset > mDirection * mEndOffset) menuOffset = mEndOffset

                val anim = SlideAnimation(menuOffset.toFloat(), mEndOffset.toFloat())
                anim.setAnimationListener(mOpenListener)
                startAnimation(anim)
	
				//打开菜单后显示Mask
				toggleMask(true)
            } else {
                if (mDirection * menuOffset < mDirection * mStartOffset) menuOffset = mStartOffset

                val anim = SlideAnimation(menuOffset.toFloat(), mStartOffset.toFloat())
                anim.setAnimationListener(mCloseListener)
                startAnimation(anim)
            }
        } else {//拖拽关闭菜单
            if (mDirection * menuOffset < mDirection * mStartOffset -10) {//拖拽超数值过N，关闭菜单
                if (mDirection * menuOffset < mDirection * mEndOffset) menuOffset = mEndOffset

                val anim = SlideAnimation(menuOffset.toFloat(), mEndOffset.toFloat())
                anim.setAnimationListener(mCloseListener)
                startAnimation(anim)

                //关闭菜单后隐藏Mask
                toggleMask(false)
            } else {//滑动不到目标位置，恢复原来的位置
                if (mDirection * menuOffset > mDirection * mStartOffset) menuOffset = mStartOffset

                val anim = SlideAnimation(menuOffset.toFloat(), mStartOffset.toFloat())
                anim.setAnimationListener(mOpenListener)
                startAnimation(anim)
            }
        }
    }

    private inner class SlideAnimation(private val mStart: Float, private val mEnd: Float) : Animation() {

        init {
            val SPEED = 2.5f
            interpolator = DecelerateInterpolator()

            val duration = Math.abs(mEnd - mStart) / SPEED
            setDuration(duration.toLong())
        }

        override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
            super.applyTransformation(interpolatedTime, t)

            val offset = (mEnd - mStart) * interpolatedTime + mStart
            menuOffset = offset.toInt()

            postInvalidate()
        }


    }

    interface OnSlideListener {
        fun onSlideCompleted(opened: Boolean)
    }

    companion object {

        val DIRECTION_LEFT = 1
        val DIRECTION_RIGHT = -1

        protected val MODE_READY = 0
        protected val MODE_SLIDE = 1
        protected val MODE_FINISHED = 2
    }

}
